package com.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.common.core.constants.Constants;
import com.common.core.enums.ResultCode;
import com.common.security.exception.ServiceException;
import com.github.pagehelper.PageHelper;
import com.system.domain.exam.Exam;
import com.system.domain.exam.ExamQuestion;
import com.system.domain.exam.dto.ExamAddDTO;
import com.system.domain.exam.dto.ExamEditDTO;
import com.system.domain.exam.dto.ExamQueryDTO;
import com.system.domain.exam.dto.ExamQuestionAddDTO;
import com.system.domain.exam.vo.ExamDetailVO;
import com.system.domain.exam.vo.ExamVO;
import com.system.domain.question.Question;
import com.system.domain.question.vo.QuestionVO;
import com.system.manager.ExamCacheManager;
import com.system.mapper.exam.ExamMapper;
import com.system.mapper.exam.ExamQuestionMapper;
import com.system.mapper.question.QuestionMapper;
import com.system.service.exam.ExamService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements ExamService {
    @Resource
    ExamMapper examMapper;

    @Resource
    ExamQuestionMapper examQuestionMapper;

    @Resource
    QuestionMapper questionMapper;

    @Resource
    ExamCacheManager examCacheManager;

    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(),examQueryDTO.getPageSize());
        List<ExamVO> list= examMapper.selectExamList(examQueryDTO);
        return list;
    }

    @Override
    public int add(ExamAddDTO examAddDTO) {
        checkExamSave(examAddDTO,null);
        Exam exam=new Exam();
        BeanUtil.copyProperties(examAddDTO,exam);
        return examMapper.insert(exam);
    }

    @Override
    public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        Exam exam = getExam(examQuestionAddDTO.getExamId());

        checkExam(exam);

        Set<Long> questionIdSet=examQuestionAddDTO.getQuestionIdSet();
        if(CollectionUtil.isEmpty(questionIdSet)){
            //题目集合为空
            return true;
        }

        List<Question> questionList = questionMapper.selectBatchIds(questionIdSet);
        if(CollectionUtil.isEmpty(questionList) || questionList.size() < questionIdSet.size()){
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }

        List<ExamQuestion> examQuestionList=new LinkedList<>();

        //记录题目顺序
        int cnt=1;

        for(Long questionId:questionIdSet){
            ExamQuestion examQuestion=new ExamQuestion();
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(cnt++);
            examQuestionList.add(examQuestion);
        }
        return saveBatch(examQuestionList);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam=getExam(examId);

        checkExam(exam);

        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .eq(ExamQuestion::getQuestionId,questionId));
    }

    @Override
    public ExamDetailVO detail(Long examId) {
        Exam exam=getExam(examId);
        ExamDetailVO examDetailVO=new ExamDetailVO();
        BeanUtil.copyProperties(exam,examDetailVO);

        //首先根据竞赛Id，去竞赛题目关系表中查询对应竞赛Id的表
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId,examId)
                .orderByAsc(ExamQuestion::getQuestionOrder));
        if(CollectionUtil.isEmpty(examQuestionList)){
            //判断这个题目Id表是不是空的
            return examDetailVO;
        }

        //从刚刚查出的表中取出题目Id
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();

        //跟据题目Id列表，去题库中查询题目的Id，标题，难度
        List<Question> questionList=questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId,Question::getTitle,Question::getDifficulty)
                .in(Question::getQuestionId,questionIdList));

        //将刚刚查出的题目列表转换成QuestionVO
        List<QuestionVO> questionVOList = BeanUtil.copyToList(questionList,QuestionVO.class);

        //设置元素
        examDetailVO.setExamQuestionList(questionVOList);

        return examDetailVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        checkExamSave(examEditDTO,examEditDTO.getExamId());
        Exam exam=getExam(examEditDTO.getExamId());
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        exam.setExamId(examEditDTO.getExamId());
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }


    @Override
    public int delete(Long examId) {
        Exam exam=getExam(examId);
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));
        return examMapper.deleteById(exam);
    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);

        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            //竞赛已经结束
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }

        //根据竞赛Id找到该竞赛下有多少道题目
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if(count==null || count<=0){
            //说明该竞赛下没有题目,不可以发布竞赛
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        exam.setStatus(Constants.TRUE);

        //将要更新的竞赛数据同步到Redis上去，e:t:l    e:d:examId
        examCacheManager.addCache(exam);

        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);

        checkExam(exam);

        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            //竞赛已经结束
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }

        exam.setStatus(Constants.FALSE);

        examCacheManager.deleteCache(examId);

        return examMapper.updateById(exam);
    }

    private void checkExam(Exam exam){
        if(exam.getStartTime().isBefore(LocalDateTime.now())){
            //保证竞赛没有开始
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    //检查竞赛基本信息是否合法
    private void checkExamSave(ExamAddDTO examSaveDTO,Long examId) {
        List<Exam> examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle, examSaveDTO.getTitle())
                .ne(examId!=null,Exam::getExamId,examId));

        if(CollectionUtil.isNotEmpty(examList)){
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }

        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())) {
            //竞赛开始时间不能早于当前时间
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }

        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }

    //通过竞赛Id获取竞赛相关信息
    private Exam getExam(Long examId){
        Exam exam = examMapper.selectById(examId);
        if(exam==null){
            //竞赛不存在
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }
}
